package com.ruoyi.common.core.dao.dialect;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.Oracle8iDialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.SQLServerDialect;
import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.engine.jdbc.dialect.spi.DatabaseMetaDataDialectResolutionInfoAdapter;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.StringType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 数据库方言适配器抽象类，实现了DB2/Oracle/Mssqlserver三种数据库的公共函数，存在差别的函数在对应的子类中实现
 * @author Gavin liuyj
 */
public abstract class DialectAdapter implements FuncAdapter {
	protected final Logger log = LoggerFactory.getLogger(getClass());

	protected SessionFactory sessionFactory; // 数据库连接会话工厂
	protected Dialect dialect; // 当前数据库方言

	public DialectAdapter() {
		initAdapter();
	}

	public DialectAdapter(SessionFactory session) {
		this.sessionFactory = session;
		initAdapter();
	}

	/**
	 * 初始化适配器，根据数据库类型建立对应的方言解析器，子类覆盖这个方法可以直接实例化对应的方言
	 */
	protected void initAdapter() {
		//con = ((SessionFactoryImpl) session).getConnectionProvider().getConnection();
		Session session = sessionFactory.getCurrentSession();
		 session.doWork(
				connection -> {
					dialect = new HSDialectResolver().resolveDialect(new DatabaseMetaDataDialectResolutionInfoAdapter(connection.getMetaData()));
				}
		);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see tso.db.dialect.FuncAdapter#getCurrentDBType()
	 */
	public int getCurrentDBType() {
		if (dialect == null) return -1; // 如果对应数据库方言没有生成，返回-1
		if (dialect instanceof Oracle8iDialect)
			return ORACLE;
		else if (dialect instanceof SQLServerDialect)
			return MSSQL;
		else if (dialect instanceof DB2Dialect)
			return IBM_DB2;
		else if (dialect instanceof MySQLDialect){
			return MYSQL;
		}
			return -1;
	}

	/**
	 * 从函数表中查找指定函数（DB2\Oracle\MSSQL）三种数据库函数名都统一的情况
	 * @param funcName 函数名称
	 * @return 函数对象，可以直接渲染生成对应的SQL Script
	 */
	protected SQLFunction getFunc(String funcName) {
		return (SQLFunction) dialect.getFunctions().get(funcName);

	}

	/**
	 * 方便代码阅读，统一脚本渲染执行过程，私有方法
	 * @param func 数据库函数对象
	 * @param params 函数参数
	 * @return
	 */
	protected String execute(SQLFunction func, List params) {
		if (func == null) return "";
		return func.render(StringType.INSTANCE,params, (SessionFactoryImplementor) sessionFactory);
	}

	/**
	 * 通过判断传入的参数是否被单引号包括，来判断是否是对字段应用函数，还是对常量应用函数
	 * @param str
	 * @return
	 */
	protected boolean isFieldName(String str) {
		str = str.trim();
		if (str == null || str.charAt(0) == '\'')
			return false;
		else
			return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see tso.db.dialect.FuncAdapter#concat(java.util.List)
	 */
	public String concat(List params) {
		return execute(getFunc("concat"), params);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see tso.db.dialect.FuncAdapter#concat(java.lang.String,
	 * java.lang.String)
	 */
	public String concat(String str1, String str2) {
		List params = new ArrayList();
		params.add(str1);
		params.add(str2);
		return execute(getFunc("concat"), params);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see tso.db.dialect.FuncAdapter#substr(java.lang.String, int, int)
	 */
	public String substr(String str, int start, int count) {
		List params = new ArrayList();
		params.add(str);
		params.add(start);
		params.add(count);
		return execute(getFunc("substring"), params);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see tso.db.dialect.FuncAdapter#trim(java.lang.String)
	 */
	public String trim(String str) {
		List params = new ArrayList();
		params.add(str);
		return execute(getFunc("trim"), params);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see tso.db.dialect.FuncAdapter#num2String(java.lang.String)
	 */
	public String num2String(String str) {
		List params = new ArrayList();
		params.add(str);
		return execute(getFunc("str"), params);
	}

	public String castString2Num(String str) {
		List params = new ArrayList();
		params.add(str);
		//2022-2-10 update Hibernate.DOUBLE已弃用，改用StandardBasicTypes.DOUBLE
		//params.add(Hibernate.DOUBLE.getName());
		params.add(StandardBasicTypes.DOUBLE.getName());
		return execute(getFunc("cast"), params);
	}

	public String replaceNull(String field, String val) {
		return field;
	}

	public String isNull(String str) {
		return "(" + str + " is null or " + str + "='')";
	}

	public String isNotNull(String str) {
		return "(" + str + " is not null or " + str + "<>'')";
	}

}
